package jp.sourceforge.akjrcp.ui;

import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;

public abstract class CComboFieldEditor extends FieldEditor{


	    /**
	     * Validation strategy constant (value <code>0</code>) indicating that
	     * the editor should perform validation after every key stroke.
	     *
	     * @see #setValidateStrategy
	     */
	    public static final int VALIDATE_ON_KEY_STROKE = 0;

	    /**
	     * Validation strategy constant (value <code>1</code>) indicating that
	     * the editor should perform validation only when the text widget 
	     * loses focus.
	     *
	     * @see #setValidateStrategy
	     */
	    public static final int VALIDATE_ON_FOCUS_LOST = 1;

	    /**
	     * Text limit constant (value <code>-1</code>) indicating unlimited
	     * text limit and width.
	     */
	    public static int UNLIMITED = -1;

	    /**
	     * Cached valid state.
	     */
	    private boolean isValid;

	    /**
	     * Old text value.
	     */
	    private String oldValue;

	    /**
	     * The text field, or <code>null</code> if none.
	     */
	    //Text textField;
	    CCombo comboField;

	    /**
	     * Width of text field in characters; initially unlimited.
	     */
	    private int widthInChars = UNLIMITED;

	    /**
	     * Text limit of text field in characters; initially unlimited.
	     */
	    private int textLimit = UNLIMITED;

	    /**
	     * The error message, or <code>null</code> if none.
	     */
	    private String errorMessage;

	    /**
	     * Indicates whether the empty string is legal;
	     * <code>true</code> by default.
	     */
	    private boolean emptyStringAllowed = true;

	    /**
	     * The validation strategy; 
	     * <code>VALIDATE_ON_KEY_STROKE</code> by default.
	     */
	    private int validateStrategy = VALIDATE_ON_KEY_STROKE;

	    /**
	     * Creates a new string field editor 
	     */
	    protected CComboFieldEditor() {
	    }

	    /**
	     * Creates a string field editor.
	     * Use the method <code>setTextLimit</code> to limit the text.
	     * 
	     * @param name the name of the preference this field editor works on
	     * @param labelText the label text of the field editor
	     * @param width the width of the text input field in characters,
	     *  or <code>UNLIMITED</code> for no limit
	     * @param strategy either <code>VALIDATE_ON_KEY_STROKE</code> to perform
	     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
	     *  perform validation only after the text has been typed in
	     * @param parent the parent of the field editor's control
	     * @since 2.0
	     */
	    public CComboFieldEditor(String name, String labelText, int width,
	            int strategy, Composite parent) {
	        init(name, labelText);
	        widthInChars = width;
	        setValidateStrategy(strategy);
	        isValid = false;
	        errorMessage = JFaceResources
	                .getString("StringFieldEditor.errorMessage");//$NON-NLS-1$
	        createControl(parent);
	    }

	    /**
	     * Creates a string field editor.
	     * Use the method <code>setTextLimit</code> to limit the text.
	     * 
	     * @param name the name of the preference this field editor works on
	     * @param labelText the label text of the field editor
	     * @param width the width of the text input field in characters,
	     *  or <code>UNLIMITED</code> for no limit
	     * @param parent the parent of the field editor's control
	     */
	    public CComboFieldEditor(String name, String labelText, int width,
	            Composite parent) {
	        this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent);
	    }

	    /**
	     * Creates a string field editor of unlimited width.
	     * Use the method <code>setTextLimit</code> to limit the text.
	     * 
	     * @param name the name of the preference this field editor works on
	     * @param labelText the label text of the field editor
	     * @param parent the parent of the field editor's control
	     */
	    public CComboFieldEditor(String name, String labelText, Composite parent) {
	        this(name, labelText, UNLIMITED, parent);
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    protected void adjustForNumColumns(int numColumns) {
	        GridData gd = (GridData) comboField.getLayoutData();
	        gd.horizontalSpan = numColumns - 1;
	        // We only grab excess space if we have to
	        // If another field editor has more columns then
	        // we assume it is setting the width.
	        gd.grabExcessHorizontalSpace = gd.horizontalSpan == 1;
	    }

	    /**
	     * Checks whether the text input field contains a valid value or not.
	     *
	     * @return <code>true</code> if the field value is valid,
	     *   and <code>false</code> if invalid
	     */
	    protected boolean checkState() {
	        boolean result = false;
	        if (emptyStringAllowed) {
				result = true;
			}

	        if (comboField == null) {
				result = false;
			}

	        String txt = comboField.getText();

	        result = (txt.trim().length() > 0) || emptyStringAllowed;

	        // call hook for subclasses
	        result = result && doCheckState();

	        if (result) {
				clearErrorMessage();
			} else {
				showErrorMessage(errorMessage);
			}

	        return result;
	    }

	    /**
	     * Hook for subclasses to do specific state checks.
	     * <p>
	     * The default implementation of this framework method does
	     * nothing and returns <code>true</code>.  Subclasses should 
	     * override this method to specific state checks.
	     * </p>
	     *
	     * @return <code>true</code> if the field value is valid,
	     *   and <code>false</code> if invalid
	     */
	    protected boolean doCheckState() {
	        return true;
	    }

	    /**
	     * Fills this field editor's basic controls into the given parent.
	     * <p>
	     * The string field implementation of this <code>FieldEditor</code>
	     * framework method contributes the text field. Subclasses may override
	     * but must call <code>super.doFillIntoGrid</code>.
	     * </p>
	     */
	    protected void doFillIntoGrid(Composite parent, int numColumns) {
	        getLabelControl(parent);

	        comboField = getCComboControl(parent);
	        GridData gd = new GridData();
	        gd.horizontalSpan = numColumns - 1;
	        if (widthInChars != UNLIMITED) {
	            GC gc = new GC(comboField);
	            try {
	                Point extent = gc.textExtent("X");//$NON-NLS-1$
	                gd.widthHint = widthInChars * extent.x;
	            } finally {
	                gc.dispose();
	            }
	        } else {
	            gd.horizontalAlignment = GridData.FILL;
	            gd.grabExcessHorizontalSpace = true;
	        }
	        comboField.setLayoutData(gd);
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    protected void doLoad() {
	        if (comboField != null) {
	            String value = getPreferenceStore().getString(getPreferenceName());
	            comboField.setText(value);
	            oldValue = value;
	        }
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    protected void doLoadDefault() {
	        if (comboField != null) {
	            String value = getPreferenceStore().getDefaultString(
	                    getPreferenceName());
	            comboField.setText(value);
	        }
	        valueChanged();
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    protected void doStore() {
	        getPreferenceStore().setValue(getPreferenceName(), comboField.getText());
	    }

	    /**
	     * Returns the error message that will be displayed when and if 
	     * an error occurs.
	     *
	     * @return the error message, or <code>null</code> if none
	     */
	    public String getErrorMessage() {
	        return errorMessage;
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    public int getNumberOfControls() {
	        return 2;
	    }

	    /**
	     * Returns the field editor's value.
	     *
	     * @return the current value
	     */
	    public String getStringValue() {
	        if (comboField != null) {
				return comboField.getText();
			}
	        
	        return getPreferenceStore().getString(getPreferenceName());
	    }

	    /**
	     * Returns this field editor's text control.
	     *
	     * @return the text control, or <code>null</code> if no
	     * text field is created yet
	     */
	    protected CCombo getCComboControl() {
	        return comboField;
	    }

	    /**
	     * Returns this field editor's text control.
	     * <p>
	     * The control is created if it does not yet exist
	     * </p>
	     *
	     * @param parent the parent
	     * @return the text control
	     */
	    public CCombo getCComboControl(Composite parent) {
	        if (comboField == null) {
	        	comboField = new CCombo(parent,SWT.BORDER);
	        	addValues();
	        	comboField.setFont(parent.getFont());
	            switch (validateStrategy) {
	            case VALIDATE_ON_KEY_STROKE:
	            	comboField.addKeyListener(new KeyAdapter() {

	                    /* (non-Javadoc)
	                     * @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
	                     */
	                    public void keyReleased(KeyEvent e) {
	                        valueChanged();
	                    }
	                });

	                break;
	            case VALIDATE_ON_FOCUS_LOST:
	            	comboField.addKeyListener(new KeyAdapter() {
	                    public void keyPressed(KeyEvent e) {
	                        clearErrorMessage();
	                    }
	                });
	            	comboField.addFocusListener(new FocusAdapter() {
	                    public void focusGained(FocusEvent e) {
	                        refreshValidState();
	                    }

	                    public void focusLost(FocusEvent e) {
	                        valueChanged();
	                        clearErrorMessage();
	                    }
	                });
	                break;
	            default:
	                Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
	            }
	            comboField.addDisposeListener(new DisposeListener() {
	                public void widgetDisposed(DisposeEvent event) {
	                	comboField = null;
	                }
	            });
	            if (textLimit > 0) {//Only set limits above 0 - see SWT spec
	            	comboField.setTextLimit(textLimit);
	            }
	        } else {
	            checkParent(comboField, parent);
	        }
	        return comboField;
	    }

	    /**
	     * Returns whether an empty string is a valid value.
	     *
	     * @return <code>true</code> if an empty string is a valid value, and
	     *  <code>false</code> if an empty string is invalid
	     * @see #setEmptyStringAllowed
	     */
	    public boolean isEmptyStringAllowed() {
	        return emptyStringAllowed;
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    public boolean isValid() {
	        return isValid;
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    protected void refreshValidState() {
	        isValid = checkState();
	    }

	    /**
	     * Sets whether the empty string is a valid value or not.
	     *
	     * @param b <code>true</code> if the empty string is allowed,
	     *  and <code>false</code> if it is considered invalid
	     */
	    public void setEmptyStringAllowed(boolean b) {
	        emptyStringAllowed = b;
	    }

	    /**
	     * Sets the error message that will be displayed when and if 
	     * an error occurs.
	     *
	     * @param message the error message
	     */
	    public void setErrorMessage(String message) {
	        errorMessage = message;
	    }

	    /* (non-Javadoc)
	     * Method declared on FieldEditor.
	     */
	    public void setFocus() {
	        if (comboField != null) {
	        	comboField.setFocus();
	        }
	    }

	    /**
	     * Sets this field editor's value.
	     *
	     * @param value the new value, or <code>null</code> meaning the empty string
	     */
	    public void setStringValue(String value) {
	        if (comboField != null) {
	            if (value == null) {
					value = "";//$NON-NLS-1$
				}
	            oldValue = comboField.getText();
	            if (!oldValue.equals(value)) {
	            	comboField.setText(value);
	                valueChanged();
	            }
	        }
	    }

	    /**
	     * Sets this text field's text limit.
	     *
	     * @param limit the limit on the number of character in the text
	     *  input field, or <code>UNLIMITED</code> for no limit

	     */
	    public void setTextLimit(int limit) {
	        textLimit = limit;
	        if (comboField != null) {
	        	comboField.setTextLimit(limit);
			}
	    }

	    /**
	     * Sets the strategy for validating the text.
	     * <p>
	     * Calling this method has no effect after <code>createPartControl</code>
	     * is called. Thus this method is really only useful for subclasses to call
	     * in their constructor. However, it has public visibility for backward 
	     * compatibility.
	     * </p>
	     *
	     * @param value either <code>VALIDATE_ON_KEY_STROKE</code> to perform
	     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
	     *  perform validation only after the text has been typed in
	     */
	    public void setValidateStrategy(int value) {
	        Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST
	                || value == VALIDATE_ON_KEY_STROKE);
	        validateStrategy = value;
	    }

	    /**
	     * Shows the error message set via <code>setErrorMessage</code>.
	     */
	    public void showErrorMessage() {
	        showErrorMessage(errorMessage);
	    }

	    /**
	     * Informs this field editor's listener, if it has one, about a change
	     * to the value (<code>VALUE</code> property) provided that the old and
	     * new values are different.
	     * <p>
	     * This hook is <em>not</em> called when the text is initialized 
	     * (or reset to the default value) from the preference store.
	     * </p>
	     */
	    protected void valueChanged() {
	        setPresentsDefaultValue(false);
	        boolean oldState = isValid;
	        refreshValidState();

	        if (isValid != oldState) {
				fireStateChanged(IS_VALID, oldState, isValid);
			}

	        String newValue = comboField.getText();
	        if (!newValue.equals(oldValue)) {
	            fireValueChanged(VALUE, oldValue, newValue);
	            oldValue = newValue;
	        }
	    }

	    /*
	     * @see FieldEditor.setEnabled(boolean,Composite).
	     */
	    public void setEnabled(boolean enabled, Composite parent) {
	        super.setEnabled(enabled, parent);
	        getCComboControl(parent).setEnabled(enabled);
	    }
	    
	    public void add(String value){
	    	getCComboControl().add(value);
	    }
	    
	   public abstract void addValues();
	}

